// Copyright Epic Games, Inc. All Rights Reserved.
var enableRedirectionLinks = true;

const defaultConfig = {
  // The port clients connect to the matchmaking service over HTTP
  HttpPort: 80,
  UseHTTPS: false,
  // The matchmaking port the signaling service connects to the matchmaker
  MatchmakerPort: 9999,

  // Log to file
  LogToFile: true,

  EnableWebserver: true,

  appUdpPortMin: 7000,
  appUdpPortMax: 7199,
  streamPortMin: 7500,
  streamPortMax: 7699,
  cirrusHttpPortMin: 2000,
  cirrusHttpPortMax: 2199,

  maxInstances: 20, //最大允许运行的实例数
};

// Similar to the Signaling Server (SS) code, load in a config.json file for the MM parameters
const argv = require('yargs').argv;
const os = require('os');
const si = require('systeminformation');
const { v4: uuidv4 } = require('uuid');
const { Mutex } = require('async-mutex');

var configFile = typeof argv.configFile != 'undefined' ? argv.configFile.toString() : 'config.json';
const config = require('./modules/config.js').init(configFile, defaultConfig);

const process = require('process');
const express = require('express');
const cors = require('cors');
const app = express();
const http = require('http').Server(app);
const fs = require('fs');
const path = require('path');
const logging = require('./modules/logging.js');
logging.RegisterConsoleLogger();

if (config.LogToFile) {
  logging.RegisterFileLogger('./logs');
}

const portScanner = require('./modules/portScanner');

console.log(`configFile ${configFile}`);
console.log('Config: ' + JSON.stringify(config, null, '\t'));

const { spawn } = require('child_process');
app.use(express.json());
app.use(cors()); //全局启用CORS

let instances = []; //实例数组,每个实例包括一个信令服务和一个渲染应用的视频流
const appUdpPortMin = config.appUdpPortMin || 7000; //渲染应用主进程与子进程通讯的udp端口最小值
const appUdpPortMax = config.appUdpPortMax || 7199; //渲染应用主进程与子进程通讯的udp端口最大值
const streamPortMin = config.streamPortMin || 7500; //信令流媒体视频流端口最小值，用于信令服务器与渲染应用之间内部通信，前端无须感知，TCP
const streamPortMax = config.streamPortMax || 7699; //信令流媒体视频流端口最大值，用于信令服务器与渲染应用之间内部通信，前端无须感知，TCP
const cirrusHttpPortMin = config.cirrusHttpPortMin || 2000; //信令服务前端访问端口最小值，用于前端客户端与信令服务器之间的WebSocket握手和静态资源访问，TCP
const cirrusHttpPortMax = config.cirrusHttpPortMax || 2199; //信令服务前端访问端口最大值，用于前端客户端与信令服务器之间的WebSocket握手和静态资源访问，TCP

// A list of all the Cirrus server which are connected to the Matchmaker.
var cirrusServers = new Map();

// const startQueue = []; // 启动请求队列
// const maxQueueLength = 3; // 队列最大长度
// const startMutex = new Mutex();
// let isProcessingQueue = false; // 防止重复调用处理队列

let pendingInstancesCount = 0; // 正在启动的实例
const instanceMutex = new Mutex(); // 用于保护 instances 和 pendingInstancesCount

//
// Parse command line.
//

if (typeof argv.HttpPort != 'undefined') {
  config.HttpPort = argv.HttpPort;
}
if (typeof argv.MatchmakerPort != 'undefined') {
  config.MatchmakerPort = argv.MatchmakerPort;
}

http.listen(config.HttpPort, () => {
  console.log('HTTP listening on *:' + config.HttpPort);
});

if (config.UseHTTPS) {
  //HTTPS certificate details
  const options = {
    key: fs.readFileSync(path.join(__dirname, './certificates/client-key.pem')),
    cert: fs.readFileSync(path.join(__dirname, './certificates/client-cert.pem')),
  };

  var https = require('https').Server(options, app);

  //Setup http -> https redirect
  console.log('Redirecting http->https');
  app.use(function (req, res, next) {
    if (!req.secure) {
      if (req.get('Host')) {
        var hostAddressParts = req.get('Host').split(':');
        var hostAddress = hostAddressParts[0];
        return res.redirect(['https://', hostAddress, req.originalUrl].join(''));
      } else {
        console.error(
          `unable to get host name from header. Requestor ${req.ip}, url path: '${req.originalUrl}', available headers ${JSON.stringify(req.headers)}`,
        );
        return res.status(400).send('Bad Request');
      }
    }
    next();
  });

  https.listen(443, function () {
    console.log('Https listening on 443');
  });
}

let htmlDirectory = 'html/sample';
if (config.EnableWebserver) {
  // Setup folders

  if (fs.existsSync('html/custom')) {
    app.use(express.static(path.join(__dirname, '/html/custom')));
    htmlDirectory = 'html/custom';
  } else {
    app.use(express.static(path.join(__dirname, '/html/sample')));
  }
}

// No servers are available so send some simple JavaScript to the client to make
// it retry after a short period of time.
function sendRetryResponse(res) {
  // find check if a custom template should be used or the sample one
  let html = fs.readFileSync(`${htmlDirectory}/queue/queue.html`, { encoding: 'utf8' });
  html = html.replace(/\$\{cirrusServers\.size\}/gm, cirrusServers.size);

  res.setHeader('content-type', 'text/html');
  res.send(html);
}

// Get a Cirrus server if there is one available which has no clients connected.
function getAvailableCirrusServer() {
  for (const cirrusServer of cirrusServers.values()) {
    if (cirrusServer.numConnectedClients === 0 && cirrusServer.ready === true) {
      // Check if we had at least 10 seconds since the last redirect, avoiding the
      // chance of redirecting 2+ users to the same SS before they click Play.
      // In other words, give the user 10 seconds to click play button the claim the server.
      if (cirrusServer.hasOwnProperty('lastRedirect')) {
        if ((Date.now() - cirrusServer.lastRedirect) / 1000 < 10) continue;
      }
      cirrusServer.lastRedirect = Date.now();

      return cirrusServer;
    }
  }

  console.log('WARNING: No empty Cirrus servers are available');
  return undefined;
}

// 统一处理端口分配逻辑
async function allocatePort(portMin, portMax, protocol, portName) {
  try {
    const port = await portScanner.findFreePort(portMin, portMax, protocol);
    console.log(`${portName}动态分配端口,端口范围: ${portMin}-${portMax}`);
    if (port === -1) {
      console.error(`${portName}无可用端口`);
    }
    console.log(`${portName}动态分配端口: ${port}`);
    return port;
  } catch (err) {
    console.error(`${portName}端口分配失败:`, err);
    return -1; // 异常时返回-1表示失败
  }
}

// 检查文件路径是否存在
function checkFilePathExists(path, name) {
  if (!fs.existsSync(path)) {
    console.error(`${name}路径不存在: ${path}`);
    return false;
  }
  console.log(`${name}: ${path}`);
  return true;
}

// 获取本地 IP 地址
function getLocalIP() {
  const interfaces = os.networkInterfaces();
  for (const name of Object.keys(interfaces)) {
    for (const iface of interfaces[name]) {
      if (iface.family === 'IPv4' && !iface.internal) {
        return iface.address;
      }
    }
  }
  return '127.0.0.1'; // 若未找到合适 IP，默认返回 127.0.0.1
}

// 日志记录请求信息
function logRequest(req, label = '请求') {
  const protocol =
    req.headers['x-forwarded-proto'] || (req.connection.encrypted ? 'https' : 'http');
  const host = req.headers.host || 'localhost';
  const fullUrl = `${protocol}://${host}${req.originalUrl}`;
  console.log(`[${label}] 方法: ${req.method}, URL: ${fullUrl}`);
  //   console.log(`[Headers]`, req.headers);
  //   console.log(`[Query]`, req.query);
  console.log(`[Body]`, req.body);
}

// Handle REST signalling server only request.
// app.options('/signallingserver', cors())
// app.get('/signallingserver', cors(),  (req, res) => {
// 	cirrusServer = getAvailableCirrusServer();
// 	if (cirrusServer != undefined) {
// 		res.json({ signallingServer: `${cirrusServer.address}:${cirrusServer.port}`});
// 		console.log(`Returning ${cirrusServer.address}:${cirrusServer.port}`);
// 	} else {
// 		res.json({ signallingServer: '', error: 'No signalling servers available'});
// 	}
// });

app.get('/v1/signallingserver', (req, res) => {
  logRequest(req, 'REST请求');
  const cirrusServer = getAvailableCirrusServer();
  const serverUrl = `http://${req.hostname || 'localhost'}:${config.HttpPort}`;
  if (cirrusServer != undefined) {
    res.json({
      serverUrl,
      signallingServer: `${cirrusServer.address}:${cirrusServer.port}`,
    });
    console.log(`Returning ${cirrusServer.address}:${cirrusServer.port}`);
  } else {
    res.json({
      serverUrl,
      signallingServer: '',
      error: 'No signalling servers available',
    });
  }
});

// 启动新实例（信令 + 视频流）
app.post('/v1/instances/start', async (req, res) => {
  logRequest(req, 'REST请求');
  const { modelFilePath = '' } = req.body || {};
  const serverUrl = `http://${req.hostname || 'localhost'}:${config.HttpPort}`;

  let currentInstancesCount;
  await instanceMutex.runExclusive(async () => {
    // 获取当前活跃的实例数量
    currentInstancesCount = instances.filter((i) => i.status === 'running').length;
    // 增加待启动实例计数
    pendingInstancesCount++;
  });

  if (currentInstancesCount + pendingInstancesCount > config.maxInstances) {
    const errorMessage = `无法启动新实例：已达最大实例数限制（${config.maxInstances}）`;
    console.log(errorMessage);
    await instanceMutex.runExclusive(async () => {
      pendingInstancesCount--;
    });
    console.log(`待启动实例数减少，当前待启动实例数: ${pendingInstancesCount}`);
    return res.status(503).json({
      //503错误一般是临时、可恢复的错误，服务器本身没问题，只是暂时无法处理请求
      serverUrl,
      error: errorMessage,
    });
  }
  console.log(`待启动实例数增加，当前待启动实例数: ${pendingInstancesCount}`);

  //并行分配三个端口
  const [appUdpPort, streamPort, cirrusHttpPort] = await Promise.all([
    allocatePort(appUdpPortMin, appUdpPortMax, 'udp', 'appUdpPort'),
    allocatePort(streamPortMin, streamPortMax, 'tcp', 'streamPort'),
    allocatePort(cirrusHttpPortMin, cirrusHttpPortMax, 'tcp', 'cirrusHttpPort'),
  ]);

  // 检查端口有效性
  if ([appUdpPort, streamPort, cirrusHttpPort].some((p) => p === -1 || p === undefined)) {
    await instanceMutex.runExclusive(async () => {
      pendingInstancesCount--;
    });
    console.log(`待启动实例数减少，当前待启动实例数: ${pendingInstancesCount}`);
    return res.status(500).json({ error: '端口分配失败' });
  }

  // 检查可执行文件路径
  const cyberStreamEnginePath = config.cyberStreamEnginePath;
  if (!checkFilePathExists(cyberStreamEnginePath, 'cyberStreamEnginePath')) {
    await instanceMutex.runExclusive(async () => {
      pendingInstancesCount--;
    });
    console.log(`待启动实例数减少，当前待启动实例数: ${pendingInstancesCount}`);
    return res.status(500).json({ error: '实例启动失败：cyberStreamEnginePath路径不存在' });
  }

  const cyberStationCloudPath = config.cyberStationCloudPath;
  if (!checkFilePathExists(cyberStationCloudPath, 'cyberStationCloudPath')) {
    await instanceMutex.runExclusive(async () => {
      pendingInstancesCount--;
    });
    console.log(`待启动实例数减少，当前待启动实例数: ${pendingInstancesCount}`);
    return res.status(500).json({ error: '实例启动失败：cyberStationCloudPath路径不存在' });
  }

  // 启动子进程同时保留终端
  // const streamProc = spawn(
  // 	'cmd.exe',
  // 	[
  // 		'/c',
  // 		'start',
  // 		'"CyberStreamEngine"',
  // 		cyberStreamEnginePath,
  // 		'--appPath',cyberStationCloudPath,
  // 		'--appUdpPort', appUdpPort,
  // 		'--streamingUrl', 'localhost',
  // 		'--streamingPort', streamPort
  // 	],
  // 	{
  // 		detached: true,
  // 		stdio: 'ignore',
  // 		cwd: path.dirname(cyberStreamEnginePath) // 设置工作目录
  // 	}
  // );

  // const cirrusProc = spawn(
  // 	'cmd.exe',
  // 	[
  // 		'/c',
  // 		'start',
  // 		'"CirrusServer"',
  // 		'node',
  // 		path.join(__dirname, '../SignallingWebServer/cirrus.js'),
  // 		'--StreamerPort', streamPort,
  // 		'--HttpPort', cirrusHttpPort,
  // 		'--SFUPort','0',
  // 		'--UseMatchmaker', 'true',
  // 		'--MatchmakerAddress', '127.0.0.1',
  // 		'--MatchmakerPort', config.MatchmakerPort
  // 	],
  // 	{
  // 		detached: true,
  // 		stdio: 'ignore'
  // 	}
  // );

  const instanceId = uuidv4();
  let streamProc, cirrusProc; // 声明进程变量用于后续清理
  let isResponseSent = false; // 标记是否已发送响应

  try {
    //进程启动逻辑（含存活检查）
    const startProcess = (command, args, options, processName) => {
      return new Promise((resolve, reject) => {
        try {
          const proc = spawn(command, args, options);

          // 监听进程启动错误（异步）
          proc.on('error', (err) => {
            console.log(`[${instanceId}] ${processName} 进程启动失败:`, err.message);
            reject(new Error(`${processName} 进程启动失败`));
          });

          // 监听进程退出（启动后立即崩溃）
          proc.on('exit', (code, signal) => {
            if (code !== 0 || signal) {
              reject(new Error(`${processName} 进程异常退出`));
            }
          });

          //监听进程流完全关闭
          proc.on('close', (code, signal) => {
            console.log(
              `[${instanceId}] ${processName} 进程已完全关闭（Code: ${code}, Signal: ${signal}）`,
            );
          });

          // 延迟检查进程存活状态（避免启动后立即退出）
          setTimeout(() => {
            try {
              // 尝试检查进程是否存在（0信号不实际终止进程）
              if (proc.pid && process.kill(proc.pid, 0)) {
                resolve(proc);
              } else {
                reject(new Error(`${processName} 进程未成功启动`));
              }
            } catch (err) {
              // 处理进程不存在的情况（ESRCH 错误）
              if (err.code === 'ESRCH') {
                reject(new Error(`${processName} 进程已提前退出`));
              } else {
                reject(new Error(`${processName} 存活检查失败: ${err.message}`));
              }
            }
          }, 2000); // 根据实际启动时间调整延迟
        } catch (err) {
          console.error(`[${instanceId}] ${processName} 进程启动同步错误:`, err.message);
          reject(new Error(`${processName} 进程启动失败`));
        }
      });
    };

    //启动信令服务进程
    const cirrusArgs = [
      path.join(__dirname, '../SignallingWebServer/cirrus.js'),
      '--StreamerPort',
      String(streamPort),
      '--HttpPort',
      String(cirrusHttpPort),
      '--SFUPort',
      '0',
      '--UseMatchmaker',
      'true',
      '--MatchmakerAddress',
      '127.0.0.1',
      '--MatchmakerPort',
      String(config.MatchmakerPort),
    ];
    console.log(`[${instanceId}] 信令服务启动命令: node ${cirrusArgs.join(' ')}`);
    cirrusProc = await startProcess(
      'node',
      cirrusArgs,
      { detached: true, stdio: 'ignore', cwd: path.join(__dirname, '../SignallingWebServer') },
      '信令服务',
    );

    // 等待一定时间然后启动视频流进程
    await new Promise((resolve) => setTimeout(resolve, 500));

    //启动视频流进程
    const streamArgs = [
      '--appPath',
      cyberStationCloudPath,
      '--appUdpPort',
      String(appUdpPort),
      '--streamingUrl',
      'localhost',
      '--streamingPort',
      String(streamPort),
    ];
    if (modelFilePath) {
      streamArgs.push('--modelFilePath', modelFilePath);
    }
    console.log(`[${instanceId}] 视频流启动命令: ${cyberStreamEnginePath} ${streamArgs.join(' ')}`);
    streamProc = await startProcess(
      cyberStreamEnginePath,
      streamArgs,
      { detached: true, stdio: 'ignore', cwd: path.dirname(cyberStreamEnginePath) },
      '视频流',
    );

    const streamUrl = `http://${req.hostname || 'localhost'}:${cirrusHttpPort}`;

    //两个进程均启动成功，创建实例，添加到实例列表
    const instance = {
      id: instanceId,
      cirrusPid: cirrusProc.pid,
      streamPid: streamProc.pid,
      status: 'running',
      streamUrl,
      createdTime: new Date().toLocaleString('zh-CN', {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
      }),
    };
    // 加锁更新 instances 和 pendingInstancesCount
    await instanceMutex.runExclusive(async () => {
      // 添加到实例列表
      instances.push(instance);
      pendingInstancesCount--;
    });
    console.log(`[${instanceId}] 实例启动成功,实例信息:`, instance);
    console.log(`待启动实例数减少，当前待启动实例数: ${pendingInstancesCount}`);

    isResponseSent = true; // 标记响应已发送
    //返回响应信息
    res.json({
      serverUrl,
      id: instanceId,
      // address: req.hostname || 'localhost',
      // port: cirrusHttpPort,
      streamUrl,
    });
  } catch (error) {
    // 任一进程启动失败时的处理
    if (!isResponseSent) {
      isResponseSent = true; // 避免重复响应
      await instanceMutex.runExclusive(async () => {
        pendingInstancesCount--;
      });
      console.log(`待启动实例数减少，当前待启动实例数: ${pendingInstancesCount}`);
      res.status(500).json({
        serverUrl,
        error: '实例启动失败: ' + error.message,
      });
    }

    // 清理已启动的进程
    if (streamProc?.pid) {
      try {
        process.kill(streamProc.pid, 'SIGTERM');
      } catch (e) {
        if (e.code === 'ESRCH') {
          console.error(`[${instanceId}] 视频流进程已不存在，无需终止`);
        } else {
          console.error(`[${instanceId}] 视频流进程终止失败:`, e.message);
        }
      }
    }
    if (cirrusProc?.pid) {
      try {
        process.kill(cirrusProc.pid, 'SIGTERM');
      } catch (e) {
        if (e.code === 'ESRCH') {
          console.error(`[${instanceId}] 信令服务进程已不存在，无需终止`);
        } else {
          console.error(`[${instanceId}] 信令服务进程终止失败:`, e.message);
        }
      }
    }
  }

  //监听进程后续退出事件（处理启动成功后异常退出）
  const handleProcessExit = (processName, proc) => {
    return async (code, signal) => {
      console.log(`[${instanceId}] ${processName} 进程退出（Code: ${code}, Signal: ${signal}）`);
      let instance;
      await instanceMutex.runExclusive(async () => {
        instance = instances.find(
          (i) => i[processName === '视频流' ? 'streamPid' : 'cirrusPid'] === proc.pid,
        );
      });
      if (!instance) {
        return;
      }

      await instanceMutex.runExclusive(async () => {
        // 清理实例记录
        const instanceIndex = instances.findIndex((i) => i.id === instanceId);
        if (instanceIndex !== -1) {
          instances.splice(instanceIndex, 1);
          console.log(`[${instanceId}] 已清理关闭的实例记录`);
        }
      });

      // 如果是主动关闭，不kill另一个进程
      if (instance.stopping) {
        return;
      }

      // 非主动关闭，kill 另一个进程
      const otherProc = processName === '视频流' ? cirrusProc : streamProc;
      if (otherProc?.pid) {
        try {
          process.kill(otherProc.pid, 'SIGTERM');
          console.log(
            `[${instanceId}] 已终止关联的${processName === '视频流' ? '信令服务' : '视频流'}进程`,
          );
        } catch (e) {
          console.error(`[${instanceId}] 关联进程终止失败:`, e.message);
        }
      }
    };
  };

  // 绑定退出事件监听
  streamProc?.on('exit', handleProcessExit('视频流', streamProc));
  cirrusProc?.on('exit', handleProcessExit('信令服务', cirrusProc));
  //绑定 close 事件监听
  streamProc?.on('close', (code, signal) => {
    console.log(`[${instanceId}] 视频流进程所有流已关闭（Code: ${code}, Signal: ${signal}）`);
  });
  cirrusProc?.on('close', (code, signal) => {
    console.log(`[${instanceId}] 信令服务进程所有流已关闭（Code: ${code}, Signal: ${signal}）`);
  });
});

// async function processStartQueue() {
//   if (isProcessingQueue) {
//     console.log('正在处理队列，请稍等...');
//     return; // 正在处理，不重复触发
//   }

//   isProcessingQueue = true;

//   while (startQueue.length > 0) {
//     const { req, res } = startQueue.shift(); // FIFO
//     try {
//       await startMutex.runExclusive(async () => {
//         await handleInstanceStart(req, res); // 执行实例启动
//       });
//     } catch (err) {
//       console.log('处理启动请求失败:', err);
//       if (!res.headersSent) {
//         res.status(500).json({ error: '实例启动失败: ' + err.message });
//       }
//     }
//   }

//   isProcessingQueue = false;
// }

// // 启动新实例（信令 + 视频流）
// app.post('/v1/instances/start', (req, res) => {
//   logRequest(req, 'REST请求');

//   if (startQueue.length >= maxQueueLength) {
//     console.log('启动请求队列已满，拒绝请求');
//     return res.status(429).json({ error: '启动请求过多，请稍后再试' });
//   }

//   // 入队（包括req、res一起存储，确保异步处理后还能访问）
//   startQueue.push({ req, res });
//   console.log(`[队列] 收到来自 ${req.ip} 的请求，当前排队数量：${startQueue.length}`);

//   processStartQueue();
// });

//获取所有实例（信令 + 视频流）列表
app.get('/v1/instances/list', async (req, res) => {
  logRequest(req, 'REST请求');
  const serverUrl = `http://${req.hostname || 'localhost'}:${config.HttpPort}`;
  let instancesCopy = [];
  await instanceMutex.runExclusive(async () => {
    instancesCopy = [...instances]; // 创建数组副本
  });
  res.json({
    serverUrl,
    instances: instancesCopy,
  });
});

// 关闭单个实例（信令 + 视频流）
async function stopSingleInstance(instance) {
  let isCirrusStopSuccess = true;
  let isStreamStopSuccess = true;
  let errorMessage = '';

  try {
    instance.stopping = true; // 标记为正在关闭
    if (instance.cirrusPid) {
      try {
        // 尝试终止信令服务进程
        process.kill(instance.cirrusPid);
      } catch (e) {
        if (e.code !== 'ESRCH') {
          // 其他错误，信令服务进程关闭失败
          isCirrusStopSuccess = false;
        } else {
          console.log(`实例 ${instance.id} 的信令服务进程已关闭,无需再次关闭`);
        }
      }
    }
    if (instance.streamPid) {
      try {
        // 尝试终止视频流进程
        process.kill(instance.streamPid);
      } catch (e) {
        if (e.code !== 'ESRCH') {
          // 其他错误，视频流进程关闭失败
          isStreamStopSuccess = false;
        } else {
          console.log(`实例 ${instance.id} 的视频流进程已关闭,无需再次关闭`);
        }
      }
    }

    if (isCirrusStopSuccess && isStreamStopSuccess) {
      // 两个进程都成功关闭
      console.log(`已关闭实例 ${instance.id}`);
      return { success: true, error: null };
    } else {
      // 至少有一个进程关闭失败
      errorMessage = `实例 ${instance.id} 关闭失败，信令服务进程状态: ${isCirrusStopSuccess ? '成功' : '失败'}，视频流进程状态: ${isStreamStopSuccess ? '成功' : '失败'}`;
      console.log(errorMessage);
      return { success: false, error: errorMessage };
    }
  } catch (e) {
    errorMessage = `实例 ${instance.id} 停止失败，错误: ${e.message}`;
    console.log(errorMessage);
    return { success: false, error: errorMessage };
  }
}

//停止所有实例（信令 + 视频流）
async function stopAllInstances() {
  console.log('即将停止所有实例...');
  // 获取当前实例的副本，避免在遍历过程中数组被修改
  let instancesCopy = [];
  await instanceMutex.runExclusive(async () => {
    instancesCopy = [...instances]; // 创建数组副本
  });
  // 并发执行所有实例的停止操作
  const stopPromises = instancesCopy.map((instance) => stopSingleInstance(instance));
  const results = await Promise.allSettled(stopPromises);
  let successCount = 0;
  let failedCount = 0;
  let failedInstances = [];
  // 处理结果
  results.forEach((result, index) => {
    const instance = instancesCopy[index];
    if (result.status === 'fulfilled' && result.value.success) {
      successCount++;
    } else {
      failedCount++;
      const error = result.status === 'rejected' ? result.reason.message : result.value.error;
      failedInstances.push({
        id: instance.id,
        error: error,
      });
    }
  });
  console.log(`${successCount} 个实例停止成功，${failedCount} 个实例停止失败。`);
  if (failedCount > 0) {
    console.log('停止失败的实例:', failedInstances);
  }
  return {
    successCount,
    failedCount,
    failedInstances,
  };
}

app.post('/v1/instances/stop', async (req, res) => {
  logRequest(req, 'REST请求');
  const { id = '' } = req.body || {};
  const serverUrl = `http://${req.hostname || 'localhost'}:${config.HttpPort}`;

  // 检查 id 是否为空
  if (!id) {
    const errorMessage = '请求体中缺少实例ID';
    console.log(errorMessage);
    return res.status(400).json({
      serverUrl,
      error: errorMessage,
    });
  }

  const instance = instances.find((i) => i.id === id);
  if (instance) {
    console.log(`即将停止实例: ${id}...`);
    const result = await stopSingleInstance(instance);
    if (result.success) {
      res.json({
        serverUrl,
        message: `实例 ${id} 已停止`,
      });
    } else {
      res.json({
        serverUrl,
        error: result.error,
      });
    }
  } else {
    const errorMessage = `实例 ${id} 不存在`;
    console.log(errorMessage);
    res.json({
      serverUrl,
      error: errorMessage,
    });
  }
});

// 停止所有实例（信令 + 视频流）
app.post('/v1/instances/stop-all', async (req, res) => {
  logRequest(req, 'REST请求');
  try {
    const result = await stopAllInstances();
    const serverUrl = `http://${req.hostname || 'localhost'}:${config.HttpPort}`;
    res.json({
      serverUrl,
      message: `${result.successCount}个实例停止成功, ${result.failedCount}个实例停止失败。`,
      failedInstances: result.failedInstances,
    });
  } catch (error) {
    const serverUrl = `http://${req.hostname || 'localhost'}:${config.HttpPort}`;
    res.status(500).json({
      serverUrl,
      error: `停止所有实例失败, 错误: ${error.message}`,
      // failedInstances: []
    });
  }
});

// 获取 CPU 和内存负载
async function getServerLoad() {
  try {
    const [cpu, mem] = await Promise.all([si.currentLoad(), si.mem()]);

    return {
      cpu: cpu.currentLoad.toFixed(2), // 总体 CPU 使用率 (%)
      memory: {
        total: (mem.total / 1024 / 1024 / 1024).toFixed(2), // GB
        used: (mem.active / 1024 / 1024 / 1024).toFixed(2), // GB
        free: (mem.free / 1024 / 1024 / 1024).toFixed(2), // GB
        usage: ((mem.active / mem.total) * 100).toFixed(2), // 使用率 (%)
      },
      timestamp: new Date().toISOString(),
    };
  } catch (error) {
    console.error('Error fetching system load:', error.message);
    return null;
  }
}

// 获取当前负载信息
app.get('/v1/server/load', async (req, res) => {
  logRequest(req, 'REST请求');
  let runningInstancesCount, pending;
  await instanceMutex.runExclusive(async () => {
    // 活跃实例数
    runningInstancesCount = instances.filter((i) => i.status !== 'stopped').length;
    pending = pendingInstancesCount;
  });

  // 调用 getServerLoad 获取 CPU 和内存负载
  let cpuLoad = null;
  let memUsage = null;
  try {
    const serverLoad = await getServerLoad();
    if (serverLoad) {
      cpuLoad = serverLoad.cpu;
      memUsage = serverLoad.memory.usage;
    }
  } catch (error) {
    console.error('获取服务器负载信息失败:', error);
  }

  const serverUrl = `http://${req.hostname || 'localhost'}:${config.HttpPort}`;

  // //调试用 发布版本注释
  // console.log(`活跃实例数: ${runningInstancesCount},正在启动的实例数: ${pending}`);

  res.json({
    serverUrl,
    runningInstances: runningInstancesCount,
    pendingInstances: pending,
    maxInstances: config.maxInstances,
    cpuLoad,
    memUsage,
  });
});

// 获取当前服务器实例统计信息
app.get('/v1/server/status', async (req, res) => {
  logRequest(req, 'REST请求');
  let runningInstancesCount, pending;
  await instanceMutex.runExclusive(async () => {
    // 活跃实例数
    runningInstancesCount = instances.filter((i) => i.status !== 'stopped').length;
    pending = pendingInstancesCount;
  });
  const serverUrl = `http://${req.hostname || 'localhost'}:${config.HttpPort}`;
  res.json({
    serverUrl,
    runningInstances: runningInstancesCount,
    pendingInstances: pending,
    maxInstances: config.maxInstances,
  });
});

if (enableRedirectionLinks) {
  // Handle standard URL.
  app.get('/', (req, res) => {
    cirrusServer = getAvailableCirrusServer();
    if (cirrusServer != undefined) {
      let prefix = cirrusServer.https ? 'https://' : 'http://';
      const targetUrl = `${prefix}${cirrusServer.address}:${cirrusServer.port}/`;

      // 将原始请求的查询参数拼接到目标 URL
      const originalQuery = new URLSearchParams(req.query).toString();
      const redirectUrl = originalQuery ? `${targetUrl}?${originalQuery}` : targetUrl;

      res.redirect(redirectUrl);
      console.log(`Redirect to ${redirectUrl}`);
    } else {
      sendRetryResponse(res);
    }
  });

  // Handle URL with custom HTML.
  app.get('/custom_html/:htmlFilename', (req, res) => {
    cirrusServer = getAvailableCirrusServer();
    if (cirrusServer != undefined) {
      let prefix = cirrusServer.https ? 'https://' : 'http://';
      res.redirect(
        `${prefix}${cirrusServer.address}:${cirrusServer.port}/custom_html/${req.params.htmlFilename}`,
      );
      console.log(`Redirect to ${cirrusServer.address}:${cirrusServer.port}`);
    } else {
      sendRetryResponse(res);
    }
  });
}

//
// Connection to Cirrus.
//

const net = require('net');

function disconnect(connection) {
  console.log(`Ending connection to remote address ${connection.remoteAddress}`);
  connection.end();
}

const matchmaker = net.createServer((connection) => {
  connection.on('data', (data) => {
    try {
      message = JSON.parse(data);

      if (message)
        console.log(`Message TYPE: ${message.type}, Message CONTENT: ${JSON.stringify(message)}`);
    } catch (e) {
      console.log(
        `ERROR (${e.toString()}): Failed to parse Cirrus information from data: ${data.toString()}`,
      );
      disconnect(connection);
      return;
    }
    if (message.type === 'connect') {
      // A Cirrus server connects to this Matchmaker server.
      let address = message.address;
      if (address === 'localhost' || address === '127.0.0.1') {
        address = getLocalIP();
      }
      cirrusServer = {
        address: address,
        port: message.port,
        https: message.https,
        numConnectedClients: 0,
        lastPingReceived: Date.now(),
      };
      cirrusServer.ready = message.ready === true;

      // Handles disconnects between MM and SS to not add dupes with numConnectedClients = 0 and redirect users to same SS
      // Check if player is connected and doing a reconnect. message.playerConnected is a new variable sent from the SS to
      // help track whether or not a player is already connected when a 'connect' message is sent (i.e., reconnect).
      if (message.playerConnected == true) {
        cirrusServer.numConnectedClients = 1;
      }

      // Find if we already have a ciruss server address connected to (possibly a reconnect happening)
      let server = [...cirrusServers.entries()].find(
        ([key, val]) => val.address === cirrusServer.address && val.port === cirrusServer.port,
      );

      // if a duplicate server with the same address isn't found -- add it to the map as an available server to send users to.
      if (!server || server.size <= 0) {
        console.log(
          `Adding connection for ${cirrusServer.address.split('.')[0]} with playerConnected: ${message.playerConnected}`,
        );
        cirrusServers.set(connection, cirrusServer);
      } else {
        console.log(
          `RECONNECT: cirrus server address ${cirrusServer.address.split('.')[0]} already found--replacing. playerConnected: ${message.playerConnected}`,
        );
        var foundServer = cirrusServers.get(server[0]);

        // Make sure to retain the numConnectedClients from the last one before the reconnect to MM
        if (foundServer) {
          cirrusServers.set(connection, cirrusServer);
          console.log(
            `Replacing server with original with numConn: ${cirrusServer.numConnectedClients}`,
          );
          cirrusServers.delete(server[0]);
        } else {
          cirrusServers.set(connection, cirrusServer);
          console.log('Connection not found in Map() -- adding a new one');
        }
      }
    } else if (message.type === 'streamerConnected') {
      // The stream connects to a Cirrus server and so is ready to be used
      cirrusServer = cirrusServers.get(connection);
      if (cirrusServer) {
        cirrusServer.ready = true;
        console.log(`Cirrus server ${cirrusServer.address}:${cirrusServer.port} ready for use`);
      } else {
        disconnect(connection);
      }
    } else if (message.type === 'streamerDisconnected') {
      // The stream connects to a Cirrus server and so is ready to be used
      cirrusServer = cirrusServers.get(connection);
      if (cirrusServer) {
        cirrusServer.ready = false;
        console.log(
          `Cirrus server ${cirrusServer.address}:${cirrusServer.port} no longer ready for use`,
        );
      } else {
        disconnect(connection);
      }
    } else if (message.type === 'clientConnected') {
      // A client connects to a Cirrus server.
      cirrusServer = cirrusServers.get(connection);
      if (cirrusServer) {
        cirrusServer.numConnectedClients++;
        console.log(
          `Client connected to Cirrus server ${cirrusServer.address}:${cirrusServer.port}`,
        );
      } else {
        disconnect(connection);
      }
    } else if (message.type === 'clientDisconnected') {
      // A client disconnects from a Cirrus server.
      cirrusServer = cirrusServers.get(connection);
      if (cirrusServer) {
        cirrusServer.numConnectedClients--;
        console.log(
          `Client disconnected from Cirrus server ${cirrusServer.address}:${cirrusServer.port}`,
        );
        if (cirrusServer.numConnectedClients === 0) {
          // this make this server immediately available for a new client
          cirrusServer.lastRedirect = 0;
        }
      } else {
        disconnect(connection);
      }
    } else if (message.type === 'ping') {
      cirrusServer = cirrusServers.get(connection);
      if (cirrusServer) {
        cirrusServer.lastPingReceived = Date.now();
      } else {
        disconnect(connection);
      }
    } else {
      console.log('ERROR: Unknown data: ' + JSON.stringify(message));
      disconnect(connection);
    }
  });

  // A Cirrus server disconnects from this Matchmaker server.
  connection.on('error', () => {
    cirrusServer = cirrusServers.get(connection);
    if (cirrusServer) {
      cirrusServers.delete(connection);
      console.log(
        `Cirrus server ${cirrusServer.address}:${cirrusServer.port} disconnected from Matchmaker`,
      );
    } else {
      console.log(
        `Disconnected machine that wasn't a registered cirrus server, remote address: ${connection.remoteAddress}`,
      );
    }
  });
});

matchmaker.listen(config.MatchmakerPort, () => {
  console.log('Matchmaker listening on *:' + config.MatchmakerPort);
});

function closeServer(server, name) {
  return new Promise((resolve) => {
    if (!server.listening) {
      console.log(`${name} is not running.`);
      return resolve();
    }
    server.close(() => {
      console.log(`${name} server closed`);
      resolve();
    });
  });
}

// 进程退出时清理资源
async function cleanupResourcesOnExit() {
  try {
    // 等待所有实例停止
    await stopAllInstances();
    // 等待一段时间让子进程退出
    await new Promise((resolve) => setTimeout(resolve, 2000));
    //关闭 HTTP 服务器
    await closeServer(http, 'HTTP');
    //如果启用了 HTTPS，则关闭 HTTPS 服务器
    if (config.UseHTTPS) {
      await closeServer(https, 'HTTPS');
    }
    //关闭 Matchmaker TCP 服务器
    await closeServer(matchmaker, 'Matchmaker');
    console.log('所有资源已释放，准备退出...');
  } catch (error) {
    console.error('停止所有实例并关闭服务器时出错:', error);
  } finally {
    process.exit(0); //终止当前 Node.js 进程
  }
}

// 监听 SIGINT 事件（Ctrl+C）
process.on('SIGINT', () => {
  console.log('收到 SIGINT 信号，即将停止所有实例...');
  cleanupResourcesOnExit();
});

// 监听 SIGTERM 事件（系统请求终止）
process.on('SIGTERM', () => {
  console.log('收到 SIGTERM 信号，即将停止所有实例...');
  cleanupResourcesOnExit();
});

// 监听 uncaughtException 事件（未捕获的异常）
process.on('uncaughtException', (err) => {
  console.error('Uncaught exception:', err);
  cleanupResourcesOnExit();
});

// 监听 unhandledRejection 事件（未处理的 Promise 拒绝）
process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
  cleanupResourcesOnExit();
});
